Flight performed in strong wind containing multiple upwind and downwind loops. This notebook compares loop diameters to control inputs.
from flightdata import Flight, Fields
from flightanalysis import Section, Box
from json import loads
import plotly.express as px
import plotly.graph_objects as go
from flightplotting.plots import plotsec
from plotly.offline import init_notebook_mode
init_notebook_mode()
sec = Section.from_csv("examples/loops.csv")
plotsec(sec)
def grid3dplot(plots):
"""takes an n*m list of lists of 3d figures, puts them into a n*m subplot grid"""
nrows = len(plots)
ncols = len(plots[0])
fig = make_subplots(
cols=len(plots[0]),
rows=len(plots),
specs=[[{"type": "scene"} for i in range(ncols)] for j in range(nrows)]
)
sceneids = ["scene{}".format(i+1) for i in range(ncols*nrows)]
sceneids[0] = "scene"
fig.update_layout(**{"scene{}".format(i+1 if i>0 else ""):dict(aspectmode='data') for i in range(ncols*nrows)})
for ir, plotrow in enumerate(plots):
for ic, plot in enumerate(plotrow):
fig.add_traces(plot.data, cols=np.full(len(plot.data), ic+1).tolist(), rows=np.full(len(plot.data), ir+1).tolist())
return fig
Sections representing five individual loops are extracted from the flight:
from flightplotting.traces import tiptrace, meshes, obj
from plotly.subplots import make_subplots
import numpy as np
loops = [
sec.subset(309, 337),
sec.subset(309, 335),
sec.subset(512, 538),
sec.subset(360, 388),
sec.subset(460, 488)
]
Each section of flight data contains a line, a loop and a line. In order to identify the loop element a manoeuvre and corresponding template is created to align with the flight data:
from flightanalysis import Schedule, Categories, Manoeuvre, Loop, Line
from geometry import Point, Quaternion, Transformation
manoeuvre = Manoeuvre("loop", k=0, elements=[
Line(0.2, 0),
Loop(1, -1),
Line(0.2, 0)
])
template = manoeuvre.scale(250).create_template(
Transformation(Point.zeros(), Quaternion.from_euler((np.pi, 0, 0))),
np.mean([loop.data.bvx.mean() for loop in loops])
)
The temporal alignment is performed and new templates are created that are scaled to match the flown loops:
aligned = [Section.align(loop, template, 2)[1] for loop in loops]
matched = [manoeuvre.match_intention(ali[0].transform, ali)[0] for ali in aligned]
def create_template(definition, flown):
rotation = Quaternion.from_euler((np.pi, 0.0, 0.0 if flown[0].direction == "right" else np.pi))
return definition.create_template(
Transformation(flown[0].pos, rotation),
flown.data.bvx.mean()
)
def loop_template(loop_id):
return create_template(matched[loop_id].elements[1], matched[loop_id].elements[1].get_data(aligned[loop_id]))
templates = [loop_template(loop_id) for loop_id, loop in enumerate(loops) ]
3D plots of the flown loops are compared to the scaled templates:
from ipywidgets import IntSlider
from ipywidgets.embed import embed_minimal_html
def plot_comparison(template, flown):
fig=plotsec(
template
, color="blue", nmodels=10)
fig = plotsec(flown, nmodels=10).add_traces(fig.data)
return fig
def plotlp(loop_id):
return plot_comparison(templates[loop_id], aligned[loop_id])
grid3dplot([
[plotlp(0), plotlp(1)],
[plotlp(3), plotlp(4)]
]).update_layout(height=800, title="Loop Geometry Assessment 3D")